﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlTypes;
using System.Data.SqlServerCe;

namespace Kea
{
    public partial class TableReaderWriter<T> where T : TableReaderWriter<T>, new()
    {
        private static readonly Dictionary<Type, Dictionary<string, MetaData>> _allFieldNames = new Dictionary<Type, Dictionary<string, MetaData>>();
        private Dictionary<string, MetaData> _fieldNames = GetFieldNames<T>();

        private static Dictionary<string, MetaData> GetFieldNames<T1>()
        {
            Dictionary<string, MetaData> fieldNames;
            if (!_allFieldNames.TryGetValue(typeof(T1), out fieldNames))
                _allFieldNames[typeof(T1)] = fieldNames = new Dictionary<string, MetaData>();
            return fieldNames;
        }

        private struct MetaData
        {
            public SqlMetaData SqlMetaData { get; set; }
            private UInt16 _ord;
            public int Ordinal { get { return _ord; } set { _ord = (UInt16)value; } }
            public int MaxLength { get { return (int)SqlMetaData.MaxLength; } }
        }

        private int GetOrd(string fieldName)
        {
            return GetMetaData(fieldName).Ordinal;
        }

        private MetaData GetMetaData(string fieldName)
        {
            MetaData md;
            if (_fieldNames.TryGetValue(fieldName, out md)) return md;

            var rs = ReadData;
            if (_fieldNames.Count == 0)
            {
                var getMetaData = GetSqlMetaDataProvider(rs);

                for (int i = 0; i < rs.FieldCount; i++)
                    _fieldNames.Add(rs.GetName(i), new MetaData() { Ordinal = i, SqlMetaData = getMetaData(i) });
            }

            if (_fieldNames.TryGetValue(fieldName, out md)) return md;

            foreach (string key in _fieldNames.Keys)
            {
                if (!key.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) continue;

                return (_fieldNames[fieldName] = _fieldNames[key]);
            }
            throw new IndexOutOfRangeException("Could not find field '" + fieldName + "' on table '" + GetTableName() + "'.");
        }

        private Func<int, SqlMetaData> GetSqlMetaDataProvider(System.Data.IDataRecord dr)
        {
            SqlCeResultSet rs = dr as SqlCeResultSet;
            if (rs != null) return ord => rs.GetSqlMetaData(ord);

            SqlCeUpdatableRecord ur = (SqlCeUpdatableRecord)dr;
            return ord => ur.GetSqlMetaData(ord);
        }

        //private TF GetFieldBinder<TF>(string columnName) where TF : FieldBinder
        //{
        //    FieldBinder tf;
        //    return (TF)(_binders.TryGetValue(columnName, out tf) ? tf : null);
        //}
        //private TF CreateFieldBinder<TF>(string columnName, Func<string, TF> creator) where TF : FieldBinder
        //{
        //    TF tf = creator(columnName);
        //    _binders[columnName] = tf;
        //    return tf;
        //}

        //private abstract class FieldBinder
        //{
        //    public FieldBinder(string columnName)
        //    {
        //    }

        //}

        //private sealed class FieldBinderInt32 : FieldBinder
        //{
        //    public FieldBinderInt32(string columnName)
        //        : base(columnName)
        //    {
        //    }
        //    public int ReadValue() { return 0; }
        //    public void WriteValue(int value) { }
        //}

        #region GetSet

        //TODO Handel for new record.

        protected long GetInt64(string columnName) { return ReadData.GetInt64(GetOrd(columnName)); }
        protected long? GetInt64Ø(string columnName)
        {
            int o = GetOrd(columnName);
            return ReadData.IsDBNull(o) ? (long?)null : ReadData.GetInt64(o);
        }
        protected int GetInt32(string columnName) { return ReadData.GetInt32(GetOrd(columnName)); }
        protected int? GetInt32Ø(string columnName) { return ReadData[GetOrd(columnName)] as int?; }

        protected short GetInt16(string columnName) { return ReadData.GetInt16(GetOrd(columnName)); }
        protected short? GetInt16Ø(string columnName) { return ReadData[GetOrd(columnName)] as short?; }

        protected bool GetBool(string columnName) { return ReadData.GetBoolean(GetOrd(columnName)); }

        protected void SetBool(string columnName, bool value) { SetBoolØ(columnName, value); }
        protected void SetBoolØ(string columnName, bool? value) { SetValue(columnName, value); }

        protected void SetInt64(string columnName, long value) { SetInt64Ø(columnName, value); }
        protected void SetInt64Ø(string columnName, long? value) { SetValue(columnName, value); }

        protected void SetInt32(string columnName, int value) { SetInt32Ø(columnName, value); }
        protected void SetInt32Ø(string columnName, int? value) { SetValue(columnName, value); }

        protected void SetInt16(string columnName, short value) { SetInt16Ø(columnName, value); }
        protected void SetInt16Ø(string columnName, short? value) { SetValue(columnName, value); }

        protected void SetDate(string columnName, DateTime value) { SetDateØ(columnName, value); }
        protected void SetDateØ(string columnName, DateTime? value) { SetValue(columnName, value); }

        protected DateTime GetDate(string columnName) { return ReadData.GetDateTime(GetOrd(columnName)); }
        protected DateTime? GetDateØ(string columnName) { return ReadData[GetOrd(columnName)] as DateTime?; }

        protected decimal GetDecimal(string columnName) { return ReadData.GetDecimal(GetOrd(columnName)); }
        protected decimal? GetDecimalØ(string columnName) { return ReadData[GetOrd(columnName)] as decimal?; }

        protected void SetDecimal(string columnName, decimal value) { SetDecimalØ(columnName, value); }
        protected void SetDecimalØ(string columnName, decimal? value) { SetValue(columnName, value); }


        protected string GetStringØ(string columnName) { return ReadData[GetOrd(columnName)] as string; }

        protected void SetStringØ(string columnName, string value)
        {
            MetaData ma = GetMetaData(columnName);
            object v;
            if (value == null)
            {
                v = DBNull.Value;
            }
            else
            {
                int mx = ma.MaxLength;
                v = (mx == -1 || value.Length <= mx) ? value : value.Substring(0, mx);
            }

            if (UpdatableRecord == null)
                ResultSet.SetValue(ma.Ordinal, v);
            else
                UpdatableRecord.SetValue(ma.Ordinal, v);
        }
        protected void SetString(string columnName, string value)
        {
            if (value != null && value.Length == 0) value = null;
            SetStringØ(columnName, value);
        }

        protected string GetString(string columnName) { return ReadData[GetOrd(columnName)] as string ?? string.Empty; }

        //protected void SetString(string columnName, string value)
        //{
        //    ReadData.SetValue(GetOrd(columnName), string.IsNullOrEmpty(value) ? (object)DBNull.Value : value);
        //    //Ḁ ḁ Ḃ ḃ Ḅ ḅ Ḇ ḇ Ḉ ḉ Ḋ ḋ Ḍ ḍ Ḏ ḏ Ḑ ḑ Ḓ ḓ Ḕ ḕ Ḗ ḗ Ḙ ḙ Ḛ ḛ Ḝ ḝ Ḟ ḟ   
        //    //1E20 Ḡ ḡ Ḣ ḣ Ḥ ḥ Ḧ ḧ Ḩ ḩ Ḫ ḫ Ḭ ḭ Ḯ ḯ Ḱ ḱ Ḳ ḳ Ḵ ḵ Ḷ ḷ Ḹ ḹ Ḻ ḻ Ḽ ḽ Ḿ ḿ   
        //    //1E40 Ṁ ṁ Ṃ ṃ Ṅ ṅ Ṇ ṇ Ṉ ṉ Ṋ ṋ Ṍ ṍ Ṏ ṏ Ṑ ṑ Ṓ ṓ Ṕ ṕ Ṗ ṗ Ṙ ṙ Ṛ ṛ Ṝ ṝ Ṟ ṟ   
        //    //1E60 Ṡ ṡ Ṣ ṣ Ṥ ṥ Ṧ ṧ Ṩ ṩ Ṫ ṫ Ṭ ṭ Ṯ ṯ Ṱ ṱ Ṳ ṳ Ṵ ṵ Ṷ ṷ Ṹ ṹ Ṻ ṻ Ṽ ṽ Ṿ ṿ   
        //    //1E80 Ẁ ẁ Ẃ ẃ Ẅ ẅ Ẇ ẇ Ẉ ẉ Ẋ ẋ Ẍ ẍ Ẏ ẏ Ẑ ẑ Ẓ ẓ Ẕ ẕ ẖ ẗ ẘ ẙ ẚ ẛ 
        //}

        #endregion

    }
}
